%{
open Ast
%}

%token <int> NUM
%token <string> ID
%token INT VOID IF ELSE WHILE BREAK CONTINUE RETURN
%token SEMICOLON COMMA LPAREN RPAREN LBRACE RBRACE
%token ADD SUB MUL DIV MOD ASSIGN NOT AND OR
%token LT GT LE GE EQ NE
%token EOF

/* 优先级和结合性声明，解决移进规约冲突 */
%nonassoc THEN
%nonassoc ELSE
%right ASSIGN
%left OR
%left AND
%left EQ NE
%left LT GT LE GE
%left ADD SUB
%left MUL DIV MOD
%nonassoc NOT

%start program
%type <Ast.program> program

%%

program:
| function_def_list EOF { $1 }

function_def_list:
| { [] }
| function_def function_def_list { $1 :: $2 }

function_def:
| type_specifier ID LPAREN param_list RPAREN block
   { {
        return_type = $1;
        name = $2;
        params = $4;
        body = $6;
     } 
   }

type_specifier:
| INT { Int }
| VOID { Void }

param_list:
| { [] }
| param { [$1] }
| param COMMA param_list { $1 :: $3 }

param:
| INT ID { (Int, $2) }

block:
| LBRACE stmt_list RBRACE { $2 }

stmt_list:
| { [] }
| stmt stmt_list { $1 :: $2 }

stmt:
| expr SEMICOLON { ExprStmt $1 }
| block { Block $1 }
| SEMICOLON { Empty }
| INT ID ASSIGN expr SEMICOLON { VarDecl ($2, $4) }
| WHILE LPAREN expr RPAREN stmt { While ($3, $5) }
| BREAK SEMICOLON { Break }
| CONTINUE SEMICOLON { Continue }
| RETURN expr_opt SEMICOLON { Return $2 }
| IF LPAREN expr RPAREN stmt %prec THEN { If ($3, $5, None) }
| IF LPAREN expr RPAREN stmt ELSE stmt { If ($3, $5, Some $7) }

expr_opt:
| { None }
| expr { Some $1 }

expr:
| assign_expr { $1 }

assign_expr:
| ID ASSIGN assign_expr { BinaryOp (Assign, Identifier $1, $3) }
| lor_expr { $1 }

lor_expr:
| land_expr { $1 }
| lor_expr OR land_expr { BinaryOp (Or, $1, $3) }

land_expr:
| eq_expr { $1 }
| land_expr AND eq_expr { BinaryOp (And, $1, $3) }

eq_expr:
| rel_expr { $1 }
| eq_expr EQ rel_expr { BinaryOp (Eq, $1, $3) }
| eq_expr NE rel_expr { BinaryOp (Ne, $1, $3) }

rel_expr:
| add_expr { $1 }
| rel_expr LT add_expr { BinaryOp (Lt, $1, $3) }
| rel_expr GT add_expr { BinaryOp (Gt, $1, $3) }
| rel_expr LE add_expr { BinaryOp (Le, $1, $3) }
| rel_expr GE add_expr { BinaryOp (Ge, $1, $3) }

add_expr:
| mul_expr { $1 }
| add_expr ADD mul_expr { BinaryOp (Add, $1, $3) }
| add_expr SUB mul_expr { BinaryOp (Sub, $1, $3) }

mul_expr:
| unary_expr { $1 }
| mul_expr MUL unary_expr { BinaryOp (Mul, $1, $3) }
| mul_expr DIV unary_expr { BinaryOp (Div, $1, $3) }
| mul_expr MOD unary_expr { BinaryOp (Mod, $1, $3) }

unary_expr:
| postfix_expr { $1 }
| ADD unary_expr { UnaryOp (Plus, $2) }
| SUB unary_expr { UnaryOp (Minus, $2) }
| NOT unary_expr { UnaryOp (Not, $2) }

postfix_expr:
| primary_expr { $1 }
| ID LPAREN expr_list RPAREN { Call ($1, $3) }

expr_list:
| { [] }
| expr { [$1] }
| expr COMMA expr_list { $1 :: $3 }

primary_expr:
| ID { Identifier $1 }
| NUM { Number $1 }
| LPAREN expr RPAREN { Paren $2 }

%%